%option 8bit never-interactive reentrant bison-bridge bison-locations
%option extra-type="extra_t"
%option prefix="bson"
%option warn nodefault
%option noinput nounput noyywrap fast
%option noyyalloc noyyrealloc noyyfree

%{
#ifndef LEXER_L
#define LEXER_L
#endif
#include <stdlib.h>
#ifdef __cplusplus
#define _CPLUSPLUS
#endif
#define __cplusplus
#ifdef __STDC_LIMIT_MACROS
#define _STDC_LIMIT_MACROS
#endif
#undef __STDC_LIMIT_MACROS
#include <stdint.h>
#ifdef _STDC_LIMIT_MACROS
#define __STDC_LIMIT_MACROS
#endif
#ifndef _CPLUSPLUS
#undef __cplusplus
#endif
#include <stdio.h>
#include <limits.h>
#include <math.h>
#include "macros.h"
#include "buf.h"
#include "parser.h"
#include "extra.h"

   inline void *
   bsonalloc(size_t bytes, unused void * yyscanner) {
     return malloc(bytes);
   }

   inline void *
   bsonrealloc(void * ptr, size_t bytes, unused void * yyscanner) {
     return realloc(ptr, bytes);
   }

   inline void
   bsonfree(void * ptr, unused void * yyscanner) {
     free(ptr);
   }

   static void unexpected(buf_t *, YYLTYPE *, char *, int);

   static void expected(buf_t *, YYLTYPE *, char * expected, size_t expected_len, char * actual, int actual_len);

#define EXPECTED(s) do {                                                \
     LOCATIONS;                                                         \
     expected(yyextra.err, yylloc, s "", sizeof s - 1, yytext, yyleng); \
     return T_INVALID;                                                  \
   } while (0)

   static void unterminated(buf_t *, YYLTYPE *, char *, size_t);

#define UNTERMINATED(s) do {                                    \
     LOCATIONS;                                                 \
     unterminated(yyextra.err, yylloc, s "", sizeof s - 1);     \
     yyterminate();                                             \
   } while (0)


#define YY_USER_ACTION                                  \
   yyextra.first_column = yyextra.last_column + 1;      \
   yyextra.last_column += yyleng;                     

#define LOCATIONS do {                                  \
     yylloc->first_column = yyextra.first_column;       \
     yylloc->last_column = yyextra.last_column;         \
   } while (0)

#define RETURN(t) do {                          \
     LOCATIONS;                                 \
     return t;                                  \
   } while (0)

#define ESCAPED(c) do {                         \
     buf_encode_uint8(yyextra.buf, c);          \
     BEGIN(STRING);                             \
   } while (0)
%}

%x STRING
%x ESCAPE

%%

"{" RETURN(T_LEFT_BRACE);

"}" RETURN(T_RIGHT_BRACE);

, RETURN(T_COMMA);

: RETURN(T_COLON);

"[" RETURN(T_LEFT_BRACKET);

"]" RETURN(T_RIGHT_BRACKET);

\" {
  LOCATIONS;
  buf_acquire(yyextra.buf);
  yyextra.modified = 0;
  BEGIN(STRING);
}
<STRING>[^\"\0\\]+ buf_encode_binary(yyextra.buf, yytext, yyleng);
<STRING>\0 {
  buf_encode_literal(yyextra.buf, "\xC0\x80");
  yyextra.modified = !0;
}
<STRING>\\ BEGIN(ESCAPE);
<STRING>\" {
  yylloc->last_column = yyextra.last_column;
  yylval->string.bytes = buf_elems(yyextra.buf);
  yylval->string.len = buf_size(yyextra.buf);
  BEGIN(INITIAL);
  return yyextra.modified ? T_MODIFIED_STRING : T_STRING;
}

<ESCAPE>\" ESCAPED('"');
<ESCAPE>\\ ESCAPED('\\');
<ESCAPE>"/" ESCAPED('/');
<ESCAPE>b ESCAPED('\b');
<ESCAPE>f ESCAPED('\f');
<ESCAPE>n ESCAPED('\n');
<ESCAPE>r ESCAPED('\r');
<ESCAPE>t ESCAPED('\t');
<ESCAPE>u0000 {
  buf_encode_literal(yyextra.buf, "\xC0\x80");
  yyextra.modified = !0;
  BEGIN(STRING);
}
<ESCAPE>u[0-9a-fA-F]{4} {
  unsigned long int ul;

  memcpy(yyextra.hex_digits, yytext + 1, 4);
  ul = strtoul(yyextra.hex_digits, NULL, 16);
  if (ul <= 0x7F) {
    buf_encode_uint8(yyextra.buf, (uint8_t) ul);
  } else if (ul <= 0x07FF) {
    buf_encode_uint8(yyextra.buf, (uint8_t) (0xc0 | (ul >> 6)));
    buf_encode_uint8(yyextra.buf, (uint8_t) (0x80 | (ul & 0x3f)));
  } else {
    buf_encode_uint8(yyextra.buf, (uint8_t) (0xe0 | (ul >> 12)));
    buf_encode_uint8(yyextra.buf, (uint8_t) (0x80 | ((ul >> 6) & 0x3f)));
    buf_encode_uint8(yyextra.buf, (uint8_t) (0x80 | (ul & 0x3f)));
  }
  BEGIN(STRING);
}
<ESCAPE>u[0-9a-fA-F]{0,3} EXPECTED("unicode escape");

<STRING,ESCAPE><<EOF>> UNTERMINATED("string");

"-"?0 {
   yylval->uint8 = 0;
  return T_UINT8;
}

[1-9][0-9]{0,8} {
  int i;

  LOCATIONS;
  i = atoi(yytext);
#define MAX(max) ((int) (INT_MAX > max ? max : INT_MAX))
  if (i <= MAX(UINT8_MAX)) {
    yylval->uint8 = (uint8_t) i;
    return T_UINT8;
  }
  if (i <= MAX(UINT16_MAX)) {
    yylval->uint16 = (uint16_t) i;
    return T_UINT16;
  }
#undef MAX
  yylval->int32 = (int32_t) i;
  return T_INT32;
}

[1-9][0-9]* {
  long int ul;

  LOCATIONS;
  ul = strtoul(yytext, NULL, 10);
  if (ul <= INT32_MAX) {
    yylval->int32 = (int32_t) ul;
    return T_INT32;
  }
  yylval->double_ = (double) ul;
  return T_DOUBLE;
}

"-"[1-9][0-9]{0,8} {
  LOCATIONS;
  yylval->int32 = (int32_t) atoi(yytext);
  return T_INT32;
}

"-"[1-9][0-9]* {
  long int l;

  LOCATIONS;
  l = strtol(yytext, NULL, 10);
  if (INT32_MIN <= l && l <= INT32_MAX) {
    yylval->int32 = (int32_t) l;
    return T_INT32;
  }
  yylval->double_ = (double) l;
  return T_DOUBLE;
}

(("-")?)(0|([1-9][0-9]*))((\.[0-9]+)?)(([eE][-+]?[0-9]+)?) {
  LOCATIONS;
  yylval->double_ = atof(yytext);
  return T_DOUBLE;
}
(("-")?)(0|([1-9][0-9]*))((\.[0-9]+)?)[eE][-+]? EXPECTED("exponential digits");
(("-")?)(0|([1-9][0-9]*))\. EXPECTED("fractional digits");

"true" RETURN(T_TRUE);
"tru" |
"tr"  |
"t"   { EXPECTED("true"); }

"false" RETURN(T_FALSE);
"fals" |
"fal"  |
"fa"   |
"f"    { EXPECTED("false"); }

"null" RETURN(T_NULL);
"nul" |
"nu"  |
"n"   { EXPECTED("null"); }

[ \t\n\r]+ LOCATIONS;

<*>.|\n {
  LOCATIONS;
  unexpected(yyextra.err, yylloc, yytext, yyleng);
  return T_INVALID;
}

%%

#undef yylval
#undef yylloc

static void
lexer_error(buf_t * err, YYLTYPE * yylloc) {
  int first_column, last_column;

  if (buf_size(err) != 0) {
    buf_encode_literal(err, ", ");
  }
  buf_encode_literal(err, "error at ");
  first_column = yylloc->first_column;
  last_column = yylloc->last_column;
  if (first_column == last_column) {
    buf_encode_printf(err, "%d", first_column);
  } else {
    buf_encode_printf(err, "%d-%d",
                      first_column, last_column);
  }
}

static void
unexpected(buf_t * err, YYLTYPE * yylloc, char * s, int len) {
  lexer_error(err, yylloc);
  buf_encode_literal(err, ": lexer error, unexpected ");
  buf_encode_binary(err, s, len);
}

static void
expected(buf_t * err, YYLTYPE * yylloc, char * expected, size_t expected_len, char * actual, int actual_len) {
  unexpected(err, yylloc, actual, actual_len);
  buf_encode_literal(err, ", expected ");
  buf_encode_binary(err, expected, expected_len);
}

static void
unterminated(buf_t * err, YYLTYPE * yylloc, char * s, size_t len) {
  lexer_error(err, yylloc);
  buf_encode_literal(err, ": lexer error, unterminated ");
  buf_encode_binary(err, s, len);
}
